#pragma once

#include <iostream>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

#include <functional>
#include <cstdlib>
#include <wait.h>
#include <signal.h>
#include <pthread.h>

#include "ThreadPool_v4.hpp"
#include "Task.hpp"
#include "err.hpp"
#include "log.hpp"

namespace ns_sever
{
    static const uint16_t defaultport = 8888;
    static const int backlog = 32; // TODO

    using func_t = std::function<std::string(const std::string &)>;
    class TcpSever;

    class ThreadData
    {
    public:
        ThreadData(int fd,const std::string& ip,const uint16_t port,TcpSever *t)
        :sock(fd),clientip(ip),clientport(port),ts(t)
        {}
    public:
        int sock;
        uint16_t clientport;
        std::string clientip;
        class TcpSever* ts;

    };
    class TcpSever
    {
    public:
        TcpSever(func_t func, uint16_t port = defaultport)
            : port_(port), quit_(true), func_(func)
        {
        }
        void init()
        {
            // 1.创建套接字,文件方面
            listensock = socket(AF_INET, SOCK_STREAM, 0);

            if (listensock < 0)
            {
                std::cerr << "create error" << strerror(errno) << std::endl;
                exit(SOCKET_ERR);
            }

            // 2.绑定
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET; // 是一个unsigned short int
            local.sin_port = htons(port_);
            local.sin_addr.s_addr = INADDR_ANY; // uint32_t
            if (bind(listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                std::cerr << "bind error" << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            // 此时绑定成功
            // 3.建立连接,监听
            if (listen(listensock, backlog) < 0)
            {
                std::cerr << "sock error" << strerror(errno) << std::endl;
                exit(LISTEN_ERR);
            }
        }
        void start()
        {
            // signal(SIGCHLD,SIG_IGN); //这种方式是OK的


            quit_ = false;
            while (!quit_)
            {
                // 一定有人连我,你必须要知道他是谁
                // 4.获取连接
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int sock = accept(listensock, (struct sockaddr *)&client, &len);  //输入输出型参数
                if (sock < 0)
                {
                    std::cerr << "accept error" << strerror(errno) << std::endl;
                    continue;
                }
                // 提取client信息
                 std::string clientip = inet_ntoa(client.sin_addr);
                 uint16_t clientport = ntohs(client.sin_port);
                // std::cout << "连接成功" << sock << " from " << listensock << " , " << clientip << "-" << clientport << std::endl;
                logmessage(INFO," 连接成功%d from %d ,%s - %d",sock,listensock,clientip.c_str(),clientport);
                

                // 5.获取连接成功 进行业务处理
                //v1
                service(sock,clientip,clientport);


                //v2 多进程版本
                // pid_t fd=fork();
                // if(fd > 0) //父进程的fd会被子进程继承 不会用同一张fd表
                // {
                //     //一定要关闭不需要的fd，否则会文件描述符泄漏
                //     close(sock); //不关心
                //     pid_t ret=waitpid(fd,nullptr,0); //阻塞的,可以是(fd,nullptr,WNOHANG)——非阻塞


                //     if(ret == fd) std::cout<<"wait success"<<std::endl;
                // }
                // else if(fd == 0)
                // {
                //     //建议关闭不需要的fd
                //     close(listensock);
                //     if(fork()>0) exit(0);//所有进程自动升辈分 此时的父亲进程被退出，waitpid的爷爷成功将其回收
                //     //下面是孙子进程开始执行service
                //     service(sock,clientip,clientport);
                //     exit(0);
                // }
                // else 
                // {
                //     close(sock);
                //     continue;
                // }


                //v3 多线程——原生多线程
                //绝对不能关闭不必须的套接字，只能关自己的套接字（因为共享）
                //回收线程又会导致阻塞 和之前的问题一样 但是可以分离
                // pthread_t pid;
                // ThreadData * td=new ThreadData(sock,clientip,clientport,this);//一定要new出一个新空间给线程 因为不保证此时在不断获取sock

                // pthread_create(&pid,nullptr,threadRoutin,td);
                // delete td;

                //v4 线程池——已经有线程之后 来了之前就创建
                //使用线程池一定是有限任务 适用于较短的任务

                // Task t(sock,clientip,clientport,std::bind(&TcpSever::service,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3)); //要把service传给这个任务 将来让他在线程里执行

                // ThreadPool<Task>::GetInstance()->PushTask(t);



            }
        }
        static void * threadRoutin(void * args)
        {
            pthread_detach(pthread_self());
            ThreadData *td=static_cast<ThreadData*>(args);
            td->ts->service(td->sock,td->clientip,td->clientport);
        }
        void service(int sock,const std::string & clientip,const uint16_t& clientport)
        {
            std::string who=clientip + " - " + std::to_string(clientport); 
            while (true)
            {
                // 从sock中读取
                char buffer[1024];
                ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = '\0';
                    std::string res = func_(buffer);      // 执行回调函数
                    write(sock, res.c_str(), res.size()); // 把结果写回去
                    std::cout <<who<<">>>"<< res << std::endl;
                }
                else if (n == 0)
                {
                    // 说明对方的连接关闭
                    close(sock);
                    std::cout <<who<< " "<<"quit,me too" << std::endl;
                    break;
                }
                else
                {
                    std::cerr << "read error" << strerror(errno) << std::endl;
                    exit(READ_ERR);
                }
            }
        }
        ~TcpSever()
        {
        }

    private:
        // 云服务器不需要指定ip
        uint16_t port_; // 标识服务器上进程的唯一性
        int listensock;
        bool quit_;
        func_t func_; // 不需要自己写 交给应用层去写
    };
}